package phase1.part2;

import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import phase1.part2.NFAFileReader.FileFormatException;

/**
 * A DFAFileWriter takes in an input .txt file containing NFA information and writes a .txt file containing
 * information for the DFA equivalent.
 * 
 * @author Devin Wang
 * @version Nov 13, 2012
 */
public class DFAFileWriter {
    private Converter converter;
    private FileWriter writer;
    
    private Set<String> states;
    private State start;
    private List<State> accepting;
    private Map<String, List<Transition>> transitions;

    /**
     * Create a DFAFileWriter. All the magic happens within the constructor, including the actual writing of the file.
     * @param filein The path of the NFA input file
     * @param fileout The desired path for the DFA output file
     * @throws FileNotFoundException
     * @throws FileFormatException
     * @throws IOException
     */
    public DFAFileWriter(String filein, String fileout) throws FileNotFoundException, FileFormatException, IOException{
        converter = new Converter(filein);
        states = converter.getStateNames();
        start = converter.getStartDFA();
        accepting = converter.getAcceptingDFA();
        transitions = converter.getTransitionsDFA();
     
        writer = new FileWriter(fileout);
        writeToFile(writeLines());
        writer.close();
    }
    
    /**
     * Create the text for the file.
     * @return the String representation of everything that will be written to the file
     */
    private String writeLines(){
    	String out = "";
        out = writeStates(out);
        out = writeStart(out);
        out = writeAccepting(out);
        out = writeTransitions(out);
        return out;
    }
    
    /**
     * Create the text for the STATES line of the file.
     * @param toBeWritten the String to append file contents to
     */
    private String writeStates(String toBeWritten){
        toBeWritten = toBeWritten + "STATES ";
        boolean multiple = false;
        for(String state : states){
            if(multiple){
                toBeWritten = toBeWritten + ",";
            }
            toBeWritten = toBeWritten + state;
            multiple = true;
        }
        return toBeWritten + "\n\n";
    }
    
    /**
     * Create the text for the START line of the file.
     * @param toBeWritten the String to append file contents to
     */
    private String writeStart(String toBeWritten){
        return toBeWritten + "START " + start.getLabel() + "\n";
    }
    
    /**
     * Create the text for the ACCEPT lines of the file.
     * @param toBeWritten the String to append file contents to
     */
    private String writeAccepting(String toBeWritten){
        List<String> tokenClasses = new ArrayList<>();
        for(State s : accepting){
            if(!tokenClasses.contains(s.getTokenClass())){
                tokenClasses.add(s.getTokenClass());
            }
        }
        for(String tokenClass : tokenClasses){
            toBeWritten = toBeWritten + "ACCEPT " + tokenClass + " ";
            boolean multiple = false;
            for(State s : accepting){
                if(s.getTokenClass().equals(tokenClass)){
                    if(multiple){
                        toBeWritten = toBeWritten + ",";
                    }
                    toBeWritten = toBeWritten + s.getLabel();
                    multiple = true;
                }
            }
            toBeWritten = toBeWritten + "\n";
        }
        return toBeWritten + "\n";
    }
    
    /**
     * Create the text for the transition lines of the file.
     * @param toBeWritten the String to append file contents to
     */
    private String writeTransitions(String toBeWritten){
        for(String state : states){
            List<Transition> tList = transitions.get(state);
            if(tList != null){
            	for(Transition t : tList){
            		toBeWritten = toBeWritten + state + "," + t.getPattern() + "," + t.getEnd().getLabel() + "\n";
            	}
            }
        }
        return toBeWritten;
    }
    
    /**
     * Write the actual file.
     * @param out The String containing the file contents to be written
     * @throws IOException
     */
    private void writeToFile(String out) throws IOException{
        char[] buffer = new char[out.length()];
        out.getChars(0,out.length(),buffer,0);
        
        writer.write(buffer);
    }
}